/*
 *  linux/kernel/sched.c
 *
 *  (C) 1991  Linus Torvalds
 */

/*
 * 'sched.c' is the main kernel file. It contains scheduling primitives
 * (sleep_on, wakeup, schedule etc) as well as a number of simple system
 * call functions (type getpid(), which just extracts a field from
 * current-task
 */
#include <linux/sched.h>
#include <asm/io.h>        // for outb, outb_pause, inb_pause
#include <asm/system.h>    // for cli, sti, set_intr_gate, set_ldt_desc, set...
#include <linux/fdreg.h>   // for FD_DATA_OUT_R, floppy_off, floppy_on, flop...
#include <linux/kernel.h>  // for panic, printk
#include <linux/sys.h>     // for sys_alarm, sys_getegid, sys_geteuid, sys_g...
#include <signal.h>        // for SIGKILL, SIGSTOP, SIGALRM, sigaction
#include "linux/fs.h"      // for floppy_off, floppy_on, floppy_on_ticks
#include "linux/head.h"    // for desc_struct, gdt_tbl
#include "linux/mm.h"      // for PAGE_SIZE


#define _S(nr) (1<<((nr)-1))
//sigkill 和 sigstop 是不可阻断的. 其它都可阻断
// _BLOCKABLE is 0xfffbfeff
#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))


#define LATCH (1193180/HZ)

extern void mem_use(void);

extern int timer_interrupt(void);
extern int system_call(void);

union task_union {
	struct task_struct task_body;
	char stack[PAGE_SIZE];
};

static union task_union init_task = {INIT_TASK,};

long volatile jiffies=0;
long startup_time=0; //初始化时,用cmos读取的年月日时间转换成秒值得到
struct task_struct *cur_task_p = &(init_task.task_body);
struct task_struct *last_task_used_math = NULL;

struct task_struct * task_ptr_arr[NR_TASKS] = {&(init_task.task_body), };

long user_stack [ PAGE_SIZE>>2 ] ;

struct {
	long * a;
	short b;
	} stack_start = { & user_stack [PAGE_SIZE>>2] , 0x10 };
void show_task(int nr,struct task_struct * p)
{
	int i,j = 4096-sizeof(struct task_struct);

	printk("%d: pid=%d, state=%d, ",nr,p->pid,p->state);
	i=0;
	while (i<j && !((char *)(p+1))[i])
		i++;
	printk("%d (of %d) chars free in kernel stack\n\r",i,j);
}

void show_stat(void)
{
	int i;

	for (i=0;i<NR_TASKS;i++)
		if (task_ptr_arr[i])
			show_task(i,task_ptr_arr[i]);
}

/*
 *  'math_state_restore()' saves the current math information in the
 * old math state array, and gets the new ones from the current task
 */
void math_state_restore()
{
	if (last_task_used_math == cur_task_p)
		return;
	__asm__("fwait");
	if (last_task_used_math) {
		__asm__("fnsave %0"::"m" (last_task_used_math->tss.i387));
	}
	last_task_used_math=cur_task_p;
	if (cur_task_p->used_math) {
		__asm__("frstor %0"::"m" (cur_task_p->tss.i387));
	} else {
		__asm__("fninit"::);
		cur_task_p->used_math=1;
	}
}

/*
 *  'schedule()' is the scheduler function. This is GOOD CODE! There
 * probably won't be any reason to change this, as it should work well
 * in all circumstances (ie gives IO-bound processes good response etc).
 * The one thing you might take a look at is the signal-handler code here.
 *
 *   NOTE!!  Task 0 is the 'idle' task, which gets called when no other
 * tasks can run. It can not be killed, and it cannot sleep. The 'state'
 * information in task[0] is never used.
 */
void schedule(void)
{
	int i,next,c;
	struct task_struct ** p;

/* check alarm, wake up any interruptible tasks that have got a signal */

	for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
		if (*p) {
			if ((*p)->alarm && (*p)->alarm < jiffies) { //(*p->alarm 存储的是报警滴答数)
				(*p)->signal |= (1<<(SIGALRM-1)); //系统给任务设置ALARM 信号
					(*p)->alarm = 0;
				}
			//有信号了. 后面写法,因为_BLOCKABLE=0xfffbfeff,相与肯定有0bit,取反有非0bits
			//就是说blocked 位为0取反变成了1，这样与信号相与保持为真，把中断态任务变成running
			if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
				(*p)->state==TASK_INTERRUPTIBLE) // 可中断的任务, 当资源不满足时会睡眠
				(*p)->state=TASK_RUNNING;
		}

/* this is the scheduler proper: */

	while (1) {
		c = -1;
		next = 0;
		i = NR_TASKS;
		p = &task_ptr_arr[NR_TASKS];
		while (--i) {
			if (!*--p)
				continue;
			if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
				c = (*p)->counter, next = i;
		}
		if (c) break; //找到最大的counter 和next,
		for(p = &LAST_TASK ; p > &FIRST_TASK ; --p) //如果c为0，时间片用完,把优先级赋值给counter
			if (*p)
				(*p)->counter = ((*p)->counter >> 1) +
						(*p)->priority;
	}
	switch_to(next); //切换到该任务
}

int sys_pause(void)
{
	cur_task_p->state = TASK_INTERRUPTIBLE; //当前状态设置为可中断,重新调度
	schedule();
	return 0;
}

//功能,把当前任务放入等待队列，不可中断态,执行等待任务
void sleep_on(struct task_struct **p)
{
	struct task_struct *tmp;

	if (!p)
		return;
	if (cur_task_p == &(init_task.task_body))
		panic("task[0] trying to sleep");
	tmp = *p; //从等待队列拿下一个任务
	*p = cur_task_p; //把当前任务放入等待队列
	cur_task_p->state = TASK_UNINTERRUPTIBLE; //当前任务状态是不可中断的
	schedule(); //调度
	if (tmp)
		tmp->state=0; //如果拿下的有任务,置为running态
}

//可中断的sleep
void interruptible_sleep_on(struct task_struct **p)
{
	struct task_struct *tmp;

	if (!p)
		return;
	if (cur_task_p == &(init_task.task_body))
		panic("task[0] trying to sleep");
	tmp=*p;
	*p=cur_task_p;
repeat:	cur_task_p->state = TASK_INTERRUPTIBLE;
	schedule(); //schedule 会执行别的任务
	if (*p && *p != cur_task_p) { //回来后当前执行的任务跟保留的任务不同
		(**p).state=0; //把保留的任务置为运行态，再调度，直到该任务被执行.
		goto repeat;
	}
	*p=NULL;
	if (tmp)
		tmp->state=0;
}

void wake_up_task(struct task_struct **p)
{
	if (p && *p) {
		(**p).state=0; //把任务置为运行态
		*p=NULL;
	}
}

/*
 * OK, here are some floppy things that shouldn't be in the kernel
 * proper. They are here because the floppy needs a timer, and this
 * was the easiest way of doing it.
 */
static struct task_struct * wait_motor_task[4] = {NULL,NULL,NULL,NULL}; //等待电机的进程数组
static int  mon_timer[4]={0,0,0,0}; //电机启动所需时间
static int moff_timer[4]={0,0,0,0}; //电机维持时间
unsigned char current_DOR = 0x0C;	//bit2:使能FDC, bit3:使能DMA&IO接口

//返回电机尚需运行的时间
int floppy_on_ticks(unsigned int nr)
{
	extern unsigned char selected;
	unsigned char mask = 0x10 << nr; //nr,软盘号0-3

	if (nr>3)
		panic("floppy_on: nr>3");
	moff_timer[nr]=10000;		/* 100 s = very big :-) */
	cli();				/* use floppy_off to turn it off */
	mask |= current_DOR; //DOR->digital output registor
	if (!selected) {
		mask &= 0xFC;
		mask |= nr;
	}
	if (mask != current_DOR) { //若想等，则上次输出过该值，不等才操作.
		outb(mask,FD_DATA_OUT_R);
		if ((mask ^ current_DOR) & 0xf0)
			mon_timer[nr] = HZ/2;	//设置mon_timer为0.5秒
		else if (mon_timer[nr] < 2)
			mon_timer[nr] = 2;
		current_DOR = mask;
	}
	sti();
	return mon_timer[nr];
}
//打开磁盘,就是让软盘电机转起来
void floppy_on(unsigned int nr)
{
	cli();
	while (floppy_on_ticks(nr))
		sleep_on(nr+wait_motor_task); //挂起当前任务，大约0.5秒，执行电机任务
	sti();
}
//时间到后，关闭软盘电机
void floppy_off(unsigned int nr) // nr:0-3
{
	moff_timer[nr]=3*HZ; //300个滴答，3s, 到时间后再关闭电机
}
//如果开启了莫个电动机，则执行该磁盘定时检查
void do_floppy_timer(void)
{
	int i;
	unsigned char mask = 0x10;

	for (i=0 ; i<4 ; i++,mask <<= 1) {
		if (!(mask & current_DOR)) //检查4bit,A,B,C,D4个磁盘电动机，无标记，继续
			continue;
		if (mon_timer[i]) {
			if (!--mon_timer[i])	
				wake_up_task(i+wait_motor_task); //唤醒时间到，唤醒等待的任务
		} else if (!moff_timer[i]) {
			current_DOR &= ~mask;  // 相与，就是把某些位置低!!
			outb(current_DOR,FD_DATA_OUT_R);	//关闭时间到，关闭电机
		} else
			moff_timer[i]--;
	}
}

#define TIME_REQUESTS 64

static struct timer_list {
	long jiffies;
	void (*fn)();
	struct timer_list * next;
} timer_list[TIME_REQUESTS], * next_timer = NULL;

//向64个timer 数组中添加一个timer
void add_timer(long jiffies, void (*fn)(void))
{
	struct timer_list * p;

	if (!fn)
		return;
	cli();
	if (jiffies <= 0)
		(fn)();
	else {
		for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
			if (!p->fn)
				break;
		if (p >= timer_list + TIME_REQUESTS)
			panic("No more time requests free");
		//找到一个空闲的timer
		p->fn = fn;
		p->jiffies = jiffies;
		p->next = next_timer;
		next_timer = p;
		while (p->next && p->next->jiffies < p->jiffies) {
			//新加的timer jiffies 大时,调整一下jiffies, 然后放到后边
			p->jiffies -= p->next->jiffies;
			//把当前和下一个的fn,jiffies交换一下，保证小jiffies在前边
			fn = p->fn;
			p->fn = p->next->fn;
			p->next->fn = fn;
			jiffies = p->jiffies;
			p->jiffies = p->next->jiffies;
			p->next->jiffies = jiffies;
			p = p->next;
		}
	}
	sti();
}

void do_timer(long cpl)
{
	extern int beepcount;
	extern void sysbeepstop(void);

	if (beepcount)
		if (!--beepcount)
			sysbeepstop();

	if (cpl) //统计用户时间或系统时间
		cur_task_p->utime++;
	else
		cur_task_p->stime++;

	if (next_timer) { //当next_timer 定时到，执行next_timer 的任务，只检查一个timer?
		next_timer->jiffies--;
		while (next_timer && next_timer->jiffies <= 0) {
			void (*fn)(void);
			
			fn = next_timer->fn;
			next_timer->fn = NULL;
			next_timer = next_timer->next;
			(fn)();
		}
	}
	if (current_DOR & 0xf0) //bit7:启动电动机D,bit6:启动电动机C,bit5:启动电动机B,bit4:启动电动机A
		do_floppy_timer();  //如果开启了电动机，则执行软盘timer检查
	if ((--cur_task_p->counter)>0) return;
	cur_task_p->counter=0;
	if (!cpl) return; //是系统时间,返回
	schedule(); //是用户时间，才schedule
}
//功能,设置一个定时器，当指定时间到后，向进程发SIGALARM 信号,返回上次定时调用的剩余时间
int sys_alarm(long seconds)
{
	int old = cur_task_p->alarm;

	if (old)
		old = (old - jiffies) / HZ;
	cur_task_p->alarm = (seconds>0)?(jiffies+HZ*seconds):0;
	return (old);
}

int sys_getpid(void)
{
	return cur_task_p->pid;
}

int sys_getppid(void)
{
	return cur_task_p->father;
}

int sys_getuid(void)
{
	return cur_task_p->uid;
}

int sys_geteuid(void)
{
	return cur_task_p->euid;
}

int sys_getgid(void)
{
	return cur_task_p->gid;
}

int sys_getegid(void)
{
	return cur_task_p->egid;
}

int sys_nice(long increment)
{
	if (cur_task_p->priority-increment>0)
		cur_task_p->priority -= increment;
	return 0;
}

void sched_init(void)
{
	int i;
	struct desc_struct * p;

	if (sizeof(struct sigaction) != 16)
		panic("Struct sigaction MUST be 16 bytes");
	set_tss_desc(gdt_tbl+FIRST_TSS_ENTRY,&(init_task.task_body.tss));
	set_ldt_desc(gdt_tbl+FIRST_LDT_ENTRY,&(init_task.task_body.ldt));
	p = gdt_tbl+2+FIRST_TSS_ENTRY;
	for(i=1;i<NR_TASKS;i++) {
		task_ptr_arr[i] = NULL;
		p->a=p->b=0;
		p++;
		p->a=p->b=0;
		p++;
	}
/* Clear NT, so that we won't have troubles with that later on */
	__asm__("pushfl ; andl $0xffffbfff,(%esp) ; popfl");
	ltr(0);
	lldt(0);
	outb_pause(0x36,0x43);		/* binary, mode 3, LSB/MSB, ch 0 */
	outb_pause(LATCH & 0xff , 0x40);	/* LSB */
	outb(LATCH >> 8 , 0x40);	/* MSB */
	set_intr_gate(0x20,&timer_interrupt);
	outb(inb_pause(0x21)&~0x01,0x21);
	set_system_gate(0x80,&system_call);
}
